feat(jmap): add caldav/jmap — JMAP calendar and task client#625
feat(jmap): add caldav/jmap — JMAP calendar and task client#625SashankBhamidi wants to merge 35 commits intomasterfrom
Conversation
Introduces caldav/jmap/ as a purely additive package providing JMAP calendar support alongside the existing CalDAV client. No existing files are modified.
…nd parse_event_set to 6-tuple
Apply suggestions from code review
Apply suggestions from code review
|
@tobixen Is this better now? I’ll pull it tomorrow and work on CI errors. I committed the changes as suggestions for now. |
docs(jmap): JMAP usage documentation and autodoc stubs
…tion label; fix async docs wording
Code Review:
|
|
Thanks for the review! Responses inline:
Valid, will fix.
False positive. The UUID is a local ephemeral key within a single
Noted, out of scope for this PR.
Intentional.
Valid, will fix.
Noted, low priority.
|
…erve RELATED=END in alarm round-trip
|
Sorry for not getting back with human-generated comments yet. My 15yo son was dragging me for a monster skiing trip yesterday, we managed to get home only after midnight, quite exhausted. Will have a calmer shorter skiing trip with my daughter today before getting back to this. |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ed Fastmail claim by @tobixen
Adds
caldav/jmap/, a new module providing JMAP calendar and task support alongside the existing CalDAV client. Zero modifications to any existing file.The module follows the same layered sans-I/O design as the CalDAV side: pure method builders/parsers in
methods/, dataclasses inobjects/, bidirectional iCalendar ↔ JSCalendar conversion inconvert/, HTTP + session logic inclient.pyandasync_client.py.Usage documentation in
docs/source/jmap.rstcovers auth, event CRUD, search, incremental sync, tasks, async API, and error handling.Session bootstrap (
session.py): GET/.well-known/jmap, resolve relativeapiUrlviaurljoin(Cyrus returns a relative path), select account viaprimaryAccounts[CALENDAR_CAPABILITY]with a fallback scan of all accounts. RaisesJMAPCapabilityErrorif no calendar-capable account is found.Auth (
client.py): Basic whenusernameis supplied, Bearer when onlypasswordis given, or a pre-built auth object via theauthkwarg. No 401-challenge-retry — a 401/403 from session or API endpoint raisesJMAPAuthErrorimmediately.JMAPErrorextendsDAVErrorso existing CalDAV exception handlers catch JMAP errors too.Calendar operations on both
JMAPClientandAsyncJMAPClient:get_calendars,create_event,get_event,update_event,delete_event,search_events,get_sync_token,get_objects_by_sync_token.search_eventsuses a single batched request —CalendarEvent/query+ a result reference intoCalendarEvent/get— one HTTP round-trip regardless of result size.get_objects_by_sync_tokenraisesJMAPMethodError(error_type="serverPartialFail")when the server truncates the change list (hasMoreChanges: true).Task operations (RFC 9553):
get_task_lists,create_task,get_task,update_task,delete_task. Task methods send_TASK_USING = [CORE_CAPABILITY, TASK_CAPABILITY]; servers withouturn:ietf:params:jmap:tasksreturn an error methodResponse which_requestconverts toJMAPMethodError. Cyrus does not implement RFC 9553, so task integration tests are deferred.AsyncJMAPClient(async_client.py) mirrors every method as a coroutine. Each request opens its ownniquests.AsyncSession— no long-lived connection is held.iCalendar ↔ JSCalendar conversion (
convert/): full bidirectional mapping covering DTSTART (all-day, floating, UTC, IANA-tz, non-IANA TZID passthrough), DTEND/DURATION, RRULE, EXRULE, EXDATE, RECURRENCE-ID overrides, ORGANIZER/ATTENDEE with roles and participation status, VALARM (relative and absolute triggers), CATEGORIES, LOCATION, CLASS, TRANSP, SEQUENCE, PRIORITY, COLOR. Shared duration/datetime primitives (_timedelta_to_duration,_duration_to_timedelta,_format_local_dt) live only inconvert/_utils.py.Entry points (
__init__.py):get_jmap_client(**kwargs)andget_async_jmap_client(**kwargs)read from the same sources asget_davclient— explicit kwargs, env vars, config file — and returnNonewhen no configuration is found.254 unit tests (zero network, all mocked). 17 integration tests against live Cyrus Docker (6 sync + 6 async event CRUD/search/sync, 5 session/calendar checks); auto-skipped if server unreachable.